package com.csy.commons.handler;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

/**
 * 全局异常处理. 参考官方:
 * https://docs.spring.io/spring-boot/docs/2.2.13.RELEASE/reference/htmlsingle/#boot-features-error-handling
 *
 * @author Wu.Liang
 * @date 2022年1月18日
 */
@Slf4j
@RestControllerAdvice
public class ExceptionFilter {
	/**
	 * 错误参数异常提示
	 */
	private static final String ILLEGAL_ARGUMENT = "缺少必填参数, 或参数格式不正确";

	/**
	 * 数据校验不通过. [参数校验不通过, 比如: Assert.state(false, "message")方法验证失败]
	 *
	 * @param request
	 * @param ex
	 * @return
	 * @author Wu.Liang
	 * @date 2022年1月18日
	 */
	@ExceptionHandler(IllegalStateException.class)
	ResponseEntity<?> handleIllegalStateException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		log.error("> [{} ({})] {}", ex.getClass().getName(), status.value(), ex.getMessage());
		ex.printStackTrace();
		return getResponseEntity(ex.getMessage(), status);
	}

	/**
	 * 错误参数异常. [一般在客户端请求时缺少必填的参数会出现这个错误]
	 *
	 * @param request
	 * @param ex
	 * @return
	 * @author Wu.Liang
	 * @date 2022年1月18日
	 */
	@ExceptionHandler(IllegalArgumentException.class)
	ResponseEntity<?> handleIllegalArgumentException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		log.error("> [{} ({})] {}", ex.getClass().getName(), status.value(), ex.getMessage());
		ex.printStackTrace();
		return getResponseEntity(ILLEGAL_ARGUMENT, status);
	}

	/**
	 * 默认异常处理.
	 *
	 * @param request
	 * @param ex
	 * @return
	 * @author Wu.Liang
	 * @date 2022年1月18日
	 */
	@ExceptionHandler(Exception.class)
	ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		log.error("> [{} ({})] {}", ex.getClass().getName(), status.value(), ex.getMessage());
		ex.printStackTrace();
		return getResponseEntity(ex.getMessage(), status);
	}

	/**
	 * 根据请求封装状态码.
	 *
	 * @param request
	 * @return
	 * @author Wu.Liang
	 * @date 2022年1月18日
	 */
	private HttpStatus getStatus(HttpServletRequest request) {
		Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
		if (statusCode == null) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
		return HttpStatus.valueOf(statusCode);
	}

	/**
	 * 根据状态码和错误信息封装返回对象
	 *
	 * @param errorMsg 异常错误信息
	 * @param status   状态码
	 * @return
	 * @author Wu.Liang
	 * @date 2022年1月18日
	 */
	private ResponseEntity<?> getResponseEntity(String errorMsg, HttpStatus status) {
		// 封装头部信息
		MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(1);
		headers.set("Content-Type", MediaType.APPLICATION_JSON_VALUE);

		// 要返回给客户端的JSON信息
		String message = "{\"success\":false,\"code\":" + status.value() + ",\"message\":\"" + errorMsg + "\"}";
		return new ResponseEntity<>(message, headers, status);
	}
}
